home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 016a / m2ticker.zip / PIT.MOD
Text File  |  1991-12-01  |  15KB  |  364 lines

  1.  
  2. (* Sound and Delay routines for Fitted Software Tools Modula-2 compiler.
  3.  
  4.         The Delay, Sound, and NoSound procedures are functionally
  5.         identical to their Turbo Pascal namesakes.
  6.  
  7.         These routines directly address the Intel 8253 Programmable
  8.         Interval Timer (PIT) chip. Therefore these routines should run
  9.         faithfully on all PC-compatible systems, regardless of the CPU
  10.         or clock speed. I wrote these procedures using a a 12MHz 0ws
  11.         '286, and tested them on an 8MHz 8088.
  12.  
  13.         Your shareware contribution appreciated by the author:
  14.  
  15.                 G.S.Vigneault,
  16.                 Box 7169, Station A,
  17.                 Toronto, Ontario,
  18.                 Canada M5W 1X8
  19.  
  20.                 Send comments to...
  21.                 UUCP:   gregsv@eastern.uucp
  22.                         greg.vigneault@canrem.uucp
  23.                 ZLink:  ACCESSCOMM
  24. *)
  25.  
  26. MODULE PIT;
  27.  
  28. FROM    InOut           IMPORT  Write, WriteLn, WriteLongCard, WriteString;
  29. FROM    Keyboard        IMPORT  GetKeyCh, KeyPressed;
  30. FROM    SYSTEM          IMPORT  ASSEMBLER;
  31.  
  32.  
  33. (*------------------------------------*)
  34.  
  35. PROCEDURE Set8253( count : CARDINAL );
  36. (* The 8253 counter #0 is the heartbeat of the DOS clock. The BIOS loads
  37.    counter #0 with 0, mode 3. Some hi-res timing routines may leave it in
  38.    mode 2, which will affect these procedures. Use Set8253(0) at the very
  39.    beginning of your program to load counter#0 the same way that the BIOS
  40.    does at power up. (Sloppy TSRs might still interfere.) *)
  41.         BEGIN
  42.                 ASM
  43.                         MOV     CX, count       (* count down from this *)
  44.                         MOV     DX,40H          (* 8253 counter#0 port *)
  45.  
  46.                         CLI                     (* do not disturb *)
  47.                         MOV     AL,36H          (* counter 0, mode 3, RL=11 *)
  48.                         OUT     43H,AL          (* to 8253 control reg *)
  49.                         MOV     AL,CL
  50.                         OUT     DX,AL           (* write counter 0, LSB *)
  51.                         MOV     AL,CH
  52.                         OUT     DX,AL           (* write counter 0, MSB *)
  53.                         STI
  54.  
  55.                         MOV     ES,DX           (* far data segment *)
  56.                         MOV     AX,ES:[006CH]   (* get BIOS timer word *)
  57.                 wait:   CMP     AX,ES:[006CH]   (* wait for next tick *)
  58.                         JE      wait            (* to synchronize *)
  59.                 END
  60.         END Set8253;
  61.  
  62. (*------------------------------------*)
  63.  
  64. PROCEDURE ReadTimer( VAR count : LONGCARD );
  65. (* Returns a 32-bit representation of 8253 counter#0. By calling this
  66.    routine multiple times you may determine the elapsed time between each
  67.    call, approximately in _microseconds_ (µs). *)
  68.         BEGIN
  69.                 ASM
  70.                         MOV     DX,40H          (* 8253 count#0 port *)
  71.                         MOV     ES,DX           (* far data segment *)
  72.                         CLI                     (* do not disturb *)
  73.                         MOV     AL,06H          (* counter0, RL=00 to latch *)
  74.                         OUT     43H,AL          (* control reg: mode 3 *)
  75.                         IN      AL,DX           (* get counter#0 LSB *)
  76.                         MOV     BL,AL           (* save it *)
  77.                         IN      AL,DX           (* get counter#0 MSB *)
  78.                         STI                     (* interrupts okay now *)
  79.                         MOV     BH,AL           (* BX = counter#0 *)
  80.                         MOV     AX,ES:[006CH]   (* get counter#0 overflow *)
  81.                         NEG     BX              (* downcounter to upcounter *)
  82.                         LES     DI, count       (* pointer to longcard *)
  83.                         MOV     ES:[DI],BX      (* lower16 = counter#0 *)
  84.                         MOV     ES:[DI+2],AX    (* upper16 = BIOS word *)
  85.                 END
  86.         END ReadTimer;
  87.  
  88. (*------------------------------------*)
  89.  
  90. PROCEDURE Delay( ms : CARDINAL );
  91. (* Delays (pauses) approximately ms milliseconds before returning. I find
  92.    this to be accurate to 0.2%. You may fine-tune this procedure to your
  93.    system by adjusting the original "MOV DI,1990" load value below. *)
  94.         BEGIN
  95.                 ASM
  96.                         MOV     CX,ms           (* get the requested ms wait *)
  97.                         JCXZ    exit            (* if nothing to do *)
  98.                         MOV     DI,1990         (* this many is ~1ms *)
  99.                         MOV     DX,40H          (* counter 0 reg port *)
  100.  
  101.                 wrap:   CLI
  102.                         MOV     AL,06H          (* counter0, RL=00 to latch *)
  103.                         OUT     43H,AL          (* control reg: mode 3 *)
  104.                         IN      AL,DX           (* get counter0 LSB *)
  105.                         MOV     AH,AL           (* save it *)
  106.                         IN      AL,DX           (* get counter0 MSB *)
  107.                         STI                     (* because of 8253 design *)
  108.                         XCHG    AL,AH           (* AX = counter 0 *)
  109.                         CMP     AX,DI           (* too near end of count? *)
  110.                         JBE     wrap            (* wait (less than 1ms) *)
  111.                         MOV     SI,AX           (* save ref *)
  112.  
  113.                 wait:   CLI                     (* _must_ read 2 bytes *)
  114.                         MOV     AL,06H          (* counter0, RL=00 to latch *)
  115.                         OUT     43H,AL          (* control reg: mode 3 *)
  116.                         IN      AL,DX           (* get counter0 LSB *)
  117.                         MOV     BL,AL           (* save it *)
  118.                         IN      AL,DX           (* get counter0 MSB *)
  119.                         STI                     (* interrupts okay now *)
  120.                         MOV     BH,AL           (* now BX = counter 0 *)
  121.                         MOV     AX,SI           (* get ref val *)
  122.                         SUB     AX,BX           (* calc difference *)
  123.                         CMP     AX,DI           (* waited long enough? *)
  124.                         JB      wait            (* wait for 1ms *)
  125.  
  126.                         LOOP    wrap            (* loop for another 1ms *)
  127.                 exit:
  128.                 END
  129.         END Delay;
  130.  
  131. (*------------------------------------*)
  132.  
  133. PROCEDURE Sound( Freq : CARDINAL );
  134. (* tone of Freq frequency will continue to play until NoSound is called *)
  135.         BEGIN
  136.                 Freq    := SHORT( 1193182L DIV LONG( Freq ));
  137.  
  138.                 ASM
  139.                         MOV     DX,043H         (* 8253 control reg port *)
  140.                         MOV     AL,0B6H         (* RL=11, counter#2, mode 3 *)
  141.                         OUT     DX,AL
  142.  
  143.                         DEC     DX              (* 42H = 8253 counter#2 *)
  144.                         MOV     AX, Freq
  145.                         CLI                     (* do not disturb! *)
  146.                         OUT     DX,AL           (* Freq LSB *)
  147.                         MOV     AL,AH
  148.                         OUT     DX,AL           (* Freq MSB *)
  149.                         STI                     (* due to 8253 design *)
  150.  
  151.                         MOV     DX,061H         (* 8255 port B *)
  152.                         IN      AL,DX           (* D0=counter#2 gate enable *)
  153.                         OR      AL,3            (* D1=speaker enable *)
  154.                         OUT     DX,AL           (* set bits D0 and D1 *)
  155.                 END
  156.         END Sound;
  157.  
  158. (*------------------------------------*)
  159.  
  160. PROCEDURE NoSound;
  161. (* stops Sound by disabling speaker, and stopping 8253 counter 2 *)
  162.         BEGIN
  163.                 ASM
  164.                         MOV     DX,061H         (* 8255 port B *)
  165.                         IN      AL,DX
  166.                         AND     AL,0FCH         (* bits D0 and D1 off *)
  167.                         OUT     DX,AL
  168.                 END
  169.         END NoSound;
  170.  
  171. (*------------------------------------*)
  172.  
  173. PROCEDURE Tone( Freq, dur : CARDINAL);
  174. (* plays Freq tone for dur jiffies (1 PC jiffy = 1/18 sec ) *)
  175.         BEGIN
  176.                 Freq := SHORT( 1193182L DIV LONG( Freq ));
  177.  
  178.                 ASM
  179.                         MOV     DX,043H         (* 8253 control reg *)
  180.                         MOV     AL,0B6H
  181.                         OUT     DX,AL
  182.  
  183.                         DEC     DX              (* 42H = 8253 counter 2 *)
  184.                         MOV     AX, Freq        (* freq divider *)
  185.                         CLI                     (* do not disturb! *)
  186.                         OUT     DX,AL           (* counter#2 LSB *)
  187.                         MOV     AL,AH
  188.                         OUT     DX,AL           (* counter#2 MSB *)
  189.                         STI                     (* due to 8253 design *)
  190.  
  191.                         MOV     DX,061H         (* 8255 Port B *)
  192.                         IN      AL,DX
  193.                         OR      AL,3            (* enable counter2 and spkr *)
  194.                         OUT     DX,AL
  195.  
  196.                         SUB     AH,AH           (* get system jiffy count *)
  197.                         INT     1AH             (* via BIOS *)
  198.                         ADD     DX, dur         (* calc requested delay val *)
  199.                         MOV     BX,DX           (* save *)
  200.                 cont:
  201.                         INT     1AH             (* get current jiffies *)
  202.                         CMP     DX,BX           (* are we there yet? *)
  203.                         JNE     cont            (* loop til we are *)
  204.  
  205.                         MOV     DX,061H         (* port B bits *)
  206.                         IN      AL,DX
  207.                         AND     AL,0FCH         (* disable counter2 and spkr *)
  208.                         OUT     DX,AL
  209.                 END
  210.         END Tone;
  211.  
  212. (*------------------------------------*)
  213.  
  214. PROCEDURE GetVideoMode( VAR vmode : CARDINAL );
  215.         BEGIN
  216.                 ASM
  217.                         MOV     AH,0FH          (* get video mode fn *)
  218.                         INT     10H             (* via BIOS *)
  219.                         SUB     AH,AH           (* make it a word *)
  220.                         LES     DI, vmode       (* pointer to the variable *)
  221.                         MOV     ES:[DI],AX      (* update vmode *)
  222.                 END
  223.         END GetVideoMode;
  224.  
  225. (*------------------------------------*)
  226.  
  227. PROCEDURE SetVideoMode( Vmode : CARDINAL );
  228.         BEGIN
  229.                 ASM
  230.                         MOV     AX, Vmode       (* get requested vid mode *)
  231.                         SUB     AH,AH           (* set video mode fn *)
  232.                         INT     10H             (* via BIOS *)
  233.                 END
  234.         END SetVideoMode;
  235.  
  236. (*------------------------------------*)
  237.  
  238. PROCEDURE ClearScreen;
  239. (* text mode clear screen *)
  240.         BEGIN
  241.                 ASM
  242.                         SUB     CX,CX           (* upper left x1,y1 := 0 *)
  243.                         MOV     DL,79           (* x2 lower right *)
  244.                         MOV     DH,24           (* y2 lower right *)
  245.                         MOV     BH,7            (* attribute *)
  246.                         SUB     AL,AL           (* to blank entire window *)
  247.                         MOV     AH,6            (* using scroll *)
  248.                         INT     10H             (* via BIOS *)
  249.                 END
  250.         END ClearScreen;
  251.  
  252. (*------------------------------------*)
  253.  
  254. PROCEDURE GotoXY( Xcord, Ycord : CARDINAL );
  255. (* to place the text mode cursor *)
  256.         BEGIN
  257.                 ASM
  258.                         MOV     DX, Xcord
  259.                         MOV     AX, Ycord
  260.                         MOV     DH,AL
  261.                         SUB     BH,BH           (* display page 0 *)
  262.                         MOV     AH,2            (* set cursor position *)
  263.                         INT     10H             (* via BIOS *)
  264.                 END
  265.         END GotoXY;
  266.  
  267. (*----------------------------------------------------------------------*)
  268.  
  269. VAR
  270.         ch              : CHAR;
  271.         frequency,
  272.         OldVmode,
  273.         duration        : CARDINAL;
  274.         time1, time2    : LONGCARD;
  275.  
  276. BEGIN   (* main *)
  277.  
  278.         Set8253( 0 );              (* make sure it's the BIOS default *)
  279.  
  280.         GetVideoMode( OldVmode );       (* save current video mode *)
  281.         SetVideoMode( 2 );              (* set 80x25 B&W text *)
  282.  
  283.         ClearScreen;                    (* blank the display *)
  284.  
  285.         Sound( 440 );
  286.  
  287.         FOR duration := 1 TO 5
  288.         DO
  289.         ReadTimer( time1 );     (* to time the Delay proc *)
  290.         Delay(1000);            (* 1s = 1000ms = 1000000µs *)
  291.         ReadTimer( time2 );     (* for elapsed microseconds *)
  292.         WriteString("[Timed Delay(1000ms) of ");
  293.         WriteLongCard( time2-time1, 7 );                (* delta ReadTimer *)
  294.         WriteString("µs (microseconds)]");
  295.         WriteLn;
  296.         WriteLn;
  297.         END;
  298.  
  299.         NoSound;
  300.  
  301.         WriteLn;
  302.         WriteString("Press any key to continue...");
  303.         REPEAT (* wait *) UNTIL KeyPressed();
  304.  
  305.         ClearScreen;
  306.  
  307.         GotoXY( 37, 1 );
  308.         WriteString("ORGAN");
  309.         GotoXY( 3, 3 );
  310.         WriteString(" Use the keys in rows Q..P and A..' to play notes");
  311.         GotoXY( 3, 5 );
  312.         WriteString(" Use 1..9 keys to set note duration (in 0.054945 sec).");
  313.         GotoXY( 3, 7 );
  314.         WriteString(" Press X to EXIT.");
  315.         WriteLn;
  316.         WriteLn;
  317.  
  318.         duration := 5;
  319.  
  320.         LOOP
  321.         frequency := 0;
  322.         GetKeyCh(ch);
  323.                 IF ch <> 0C THEN
  324.                 ch := CAP(ch);
  325.                 CASE ch OF
  326.                         "Q" :   frequency := 440; |
  327.                         "W" :   frequency := 466; |
  328.                         "E" :   frequency := 494; |
  329.                         "R" :   frequency := 523; |
  330.                         "T" :   frequency := 554; |
  331.                         "Y" :   frequency := 587; |
  332.                         "U" :   frequency := 622; |
  333.                         "I" :   frequency := 659; |
  334.                         "O" :   frequency := 698; |
  335.                         "P" :   frequency := 740; |
  336.                         "A" :   frequency := 784; |
  337.                         "S" :   frequency := 831; |
  338.                         "D" :   frequency := 880; |
  339.                         "F" :   frequency := 932; |
  340.                         "G" :   frequency := 988; |
  341.                         "H" :   frequency := 1046;|
  342.                         "J" :   frequency := 1109;|
  343.                         "K" :   frequency := 1175;|
  344.                         "L" :   frequency := 1245;|
  345.                         ";" :   frequency := 1329;|
  346.                         "'" :   frequency := 1397;|
  347.                         "1".."9"
  348.                             :   duration := ORD(ch)-48;|
  349.                         "X" :   EXIT
  350.                 ELSE        ;
  351.                 END     (*case*)
  352.                 END;    (*if*)
  353.  
  354.                 IF frequency >= 100 THEN
  355.                         Write(ch);
  356.                         Tone( frequency, duration )
  357.                         END
  358.         END;    (*loop*)
  359.  
  360.         SetVideoMode( OldVmode )        (* restore original video mode *)
  361.  
  362. END PIT.
  363.  
  364.